/*!	 widget_ruler.cpp
**	 Template File
**
**	......... ... 2014 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>

#include <ETL/stringf>

#include "widgets/widget_ruler.h"

#include <gui/localization.h>

#endif

using namespace std;
using namespace synfig;
using namespace studio;

Widget_Ruler::Widget_Ruler(bool is_vertical):
    is_vertical(is_vertical),
    layout(Pango::Layout::create(get_pango_context())),
    min(0.0),
    max(0.0),
    position(0.0)
{ }

Widget_Ruler::~Widget_Ruler() { }

void
Widget_Ruler::set_min(synfig::Real value)
{
    if (min != value) {
        min = value;
        this->queue_draw();
    }
}

void
Widget_Ruler::set_max(synfig::Real value)
{
    if (max != value) {
        max = value;
        this->queue_draw();
    }
}

void
Widget_Ruler::set_position(synfig::Real value)
{
    if (position != value) {
        position = value;
        this->queue_draw();
    }
}

void
Widget_Ruler::draw_line(
    const ::Cairo::RefPtr< ::Cairo::Context>& cr,
    synfig::Real position,
    synfig::Real size,
    const Gdk::RGBA &color,
    synfig::Real width,
    synfig::Real height)
{
    position = round(position) - 0.5;

    cr->set_line_width(0.5);
    cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());

    if (is_vertical) {
        cr->move_to(width - size, position);
        cr->line_to(width, position);
    } else {
        cr->move_to(position, height - size);
        cr->line_to(position, height);
    }

    cr->stroke();
}

void
Widget_Ruler::draw_text(
    const ::Cairo::RefPtr< ::Cairo::Context>& cr,
    synfig::Real position,
    const synfig::String &text,
    int size,
    const Gdk::RGBA &color,
    synfig::Real offset,
    synfig::Real width,
    synfig::Real height)
{
    layout->set_text(text);

    int w = 0, h = 0;
    Pango::AttrList attr_list;
    Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE * size));
    pango_size.set_start_index(0);
    pango_size.set_end_index(64);
    attr_list.change(pango_size);
    layout->set_attributes(attr_list);
    layout->get_pixel_size(w, h);

    if (is_vertical) {
        cr->save();
        cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
        cr->rotate_degrees(-90.f);
        cr->move_to(-position + 3, width - offset - h);
        layout->show_in_cairo_context(cr);
        cr->restore();
    } else {
        cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
        cr->move_to(position + 3, height - offset - h);
        layout->show_in_cairo_context(cr);
    }
}

bool
Widget_Ruler::on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr)
{
    const Real min_screen_text_mark_distance = 50.0;
    const Real mark_1_size = 18.0;
    const Real mark_2_size = 18.0;
    const Real mark_3_size = 5.0;
    const Real mark_4_size = 3.0;
    const int text_size = 8;
    const Real text_offset = 5.0;

    Real screen_min = get_screen_min();
    Real screen_max = get_screen_max();

    Gdk::RGBA color = get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL);

    // Make ruler less visually noisy
    // TODO: this should probably be done via Gtk styling instead
    color.set_alpha(0.6);

    Real min_text_mark_distance = fabs(distance_from_screen(min_screen_text_mark_distance));
    int text_degree = (int)round(ceil(log10(min_text_mark_distance)));
    Real text_mark_distance = exp(log(10) * (Real)text_degree);
    Real screen_text_mark_distance = fabs(distance_to_screen(text_mark_distance));

    int mode = 0.2 * screen_text_mark_distance >= min_screen_text_mark_distance ? 2
               : 0.5 * screen_text_mark_distance >= min_screen_text_mark_distance ? 5
               : 10;
    int sub_divisions_count = 100 / mode;

    Real mark_distance = text_mark_distance / (Real)sub_divisions_count;
    Real screen_mark_distance = fabs(distance_to_screen(mark_distance));

    Real begin, end;

    if (min < max) {
        begin = floor(min / (10.0 * text_mark_distance)) * (10.0 * text_mark_distance);
        end = ceil(max / (10.0 * text_mark_distance)) * (10.0 * text_mark_distance);
    } else {
        begin = ceil(min / (10.0 * text_mark_distance)) * (10.0 * text_mark_distance);
        end = floor(max / (10.0 * text_mark_distance)) * (10.0 * text_mark_distance);
    }

    Real screen_begin = position_to_screen(begin);
    Real screen_position = get_screen_position();

    int total_marks_count = sub_divisions_count * (int)round(fabs(end - begin) / text_mark_distance) + 1;

    if (total_marks_count > 16384) {
        return true;
    }

    Real width = (Real)get_width();
    Real height = (Real)get_height();

    // draw background
    // else

    // draw bounds
    // draw_line(cr, screen_min, is_vertical ? width : height, width, height);
    // draw_line(cr, screen_max, is_vertical ? width : height, width, height);

    // draw marks
    for (int i = 0; i < total_marks_count; ++i) {
        Real screen_pos = screen_begin + (Real)i * screen_mark_distance;

        if (screen_pos <= screen_min || screen_pos >= screen_max) {
            continue;
        }

        Real pos = min < max
                   ? begin + (Real)i * mark_distance
                   : begin - (Real)i * mark_distance;

        if ((int)round(pos / mark_distance) == 0) {
            pos = 0.0;
        }

        if (i % sub_divisions_count == 0) {
            draw_line(cr, screen_pos, mark_1_size, color, width, height);
            String format = etl::strprintf("%%.%df", text_degree < 0 ? -text_degree : 0);
            String text = etl::strprintf(format.c_str(), pos);
            draw_text(cr, screen_pos, text, text_size, color, text_offset, width, height);
        } else if ((mode == 5 && i % 10 == 0)
                   || (mode == 2 && i % 10 == 0)) {
            draw_line(cr, screen_pos, mark_2_size, color, width, height);
            String format = etl::strprintf("%%.%df", text_degree < 1 ? 1 - text_degree : 0);
            String text = etl::strprintf(format.c_str(), pos);
            draw_text(cr, screen_pos, text, text_size, color, text_offset, width, height);
        } else if ((mode == 10 && i % 2 == 0)
                   || (mode ==  5 && i % 2 == 0)
                   || (mode ==  2 && i % 5 == 0)) {
            draw_line(cr, screen_pos, mark_3_size, color, width, height);
        } else {
            draw_line(cr, screen_pos, mark_4_size, color, width, height);
        }
    }

    // draw current position
    if (screen_position > screen_min && screen_position < screen_max) {
        draw_line(cr, screen_position, is_vertical ? width : height, color, width, height);
    }

    return true;
}